package view;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Point;

import javax.swing.JFrame;
import java.awt.GridLayout;


import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JOptionPane;


import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JMenuBar;
import javax.swing.JPanel;

import utils.IconNames;
import view.displayManagers.AutoGameDisplayTool;
import view.displayManagers.AutomaticGameDisplayMngr;
import view.displayManagers.ContactUser;
import view.displayManagers.InputOutputMngr;
import view.displayManagers.SinglePlayerDisplayTool;
import view.displayManagers.SingplePlayerDisplayMngr;
import view.displayManagers.SpecialGameDisplayMngr;
import view.displayManagers.SpecialGameDisplayTool;
import view.displayManagers.TwoPlayerDisplayTool;
import view.displayManagers.TwoPlayersDisplayMng;

import controller.Controller;
import controller.GameManager;

/**
 * Class used to manage the game display using GUI. The class implements interfaces that enable the functionality necessary for each client
 */
public class UserIOgui implements FrameManager, SetGameModes, GameHazardsDisplay {

	/**
	 * Class used to wrap parameters that are used to build the chess board presentation 
	 */
	private static class BoardParams {
		public Color light;
		public Color dark;
		public Color mark;
		public int size;
	}
	
	// members

	// constants
	private static final int TURN_DURATION = 1200; // duration of turn played by computer
	private static final int TURN_INTERVAL = 1200; // TODO check if used
	private static final int FRAME_SIZE = 750; // game display frame size
//	private static final String ICON_DIRECTORY = "src/view/"; // directory of board icons
//	private static final String WHITE_PAWN_ICON = ""; // image name for white Pawn icon
//	private static final String BLACK_PAWN_ICON = ""; // image name for black Pawn icon
//	private static final String WHITE_KNIGHT_ICON = ""; // image name for white Knight icon
//	private static final String BLACK_KNIGHT_ICON = ""; // image name for black Knight icon
//	private static final String WHITE_BISHOP_ICON = ""; // image name for white Bishop icon
//	private static final String BLACK_BISHOP_ICON = ""; // image name for black Bishop icon
//	private static final String WHITE_ROOK_ICON = ""; // image name for white Rook icon
//	private static final String BLACK_ROOK_ICON = ""; // image name for black Rook icon
//	private static final String WHITE_QUEEN_ICON = ""; // image name for white Queen icon
//	private static final String BLACK_QUEEN_ICON = ""; // image name for black Queen icon
//	private static final String WHITE_KING_ICON = ""; // image name for white King icon
//	private static final String BLACK_KING_ICON = ""; // image name for black King icon
//	private static final String CREATURE_ICON = ""; // image name for Creature icon
//	private static final String CREATURE_COLLIDE_ICON = ""; // image name for Creature-collision icon
//	private static final String CREATURE_ATTACK_ICON = ""; // image name for attacking Creature icon			
	
	// non-constants
	private JFrame frame; // holds the board
	private List<JButton> boardButtons; // holds button for each position on chess board
	
	private BoardParams params;
	private Controller gameManager;
	
	private Map<String, ImageIcon> iconMap;

	private InputOutputMngr ioManager;
	private TwoPlayersDisplayMng twoPlayerMngr;
	private SingplePlayerDisplayMngr singlePlayerMngr;
	private AutomaticGameDisplayMngr autoGameMngr;
	private SpecialGameDisplayMngr specialGameMngr;
	
	private AddPiecePanel piecePanel; // panel for adding pieces (no Kings)
	private AddKingsPanel kingsPanel; // panel for adding Kings 
	
	// private methods
	
	/**
	 * Initialize the contents of the frame
	 */
	private void initialize(BoardParams params) {
		
		// create a new frame
		frame = new JFrame();
		frame.setBounds(100, 100, 450, 300);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// set frame size
		frame.setSize(FRAME_SIZE, FRAME_SIZE);
		
		// disable window resize while running
		frame.setResizable(false);
		
		// set layout for the board buttons
		BorderLayout borderLayout = new BorderLayout();
		frame.getContentPane().setLayout(borderLayout);		
		JPanel boardPanel = new JPanel();
		frame.getContentPane().add(boardPanel, BorderLayout.CENTER);
		
		// build new game menu
		MenuManager menuManager = new MenuBuilder(); 
		JMenuBar menuBar = menuManager.buildNewGameMenu(this);		

		// add it to the frame
		frame.setJMenuBar(menuBar);
		
		// create grid on which buttons will be placed
		GridLayout gridLayout = new GridLayout(params.size, params.size);
		boardPanel.setLayout(gridLayout);
		
		// allocate a list to hold all board buttons
		boardButtons = new ArrayList<JButton>(params.size*params.size);

		// add board buttons
		for (int i = 0; i < params.size * params.size; ++i) {
			JButton btn = new JButton("btn" + i); // TODO maybe deleted
			boardButtons.add(btn);

			// set each button's color according to its position on board
			if ((i/params.size) % 2 == 0) {
				btn.setBackground(params.dark);
				
				if (i % 2 == 0) {
					btn.setBackground(params.light);					
				} 
			} else {
				btn.setBackground(params.dark);
				
				if (i % 2 != 0) {
					btn.setBackground(params.light);					
				}
			}
			
			// set command-string
			String cmd = String.valueOf(i);
			btn.setActionCommand(cmd);			
			btn.setText(""); // TODO empty string or null			
			boardPanel.add(btn);			
		}		
	}

	/**
	 * Removes all action listeners from board buttons
	 */
	private void removeListeners() {
		
		// scan all board buttons
		for (JButton currBtn : boardButtons) {
			
			// scan current button's action listeners list
			for (int i = 0 ; i < currBtn.getActionListeners().length ; ++i) {
				
				// remove action listener
				currBtn.removeActionListener(currBtn.getActionListeners()[i]);
			}
		}
		
		// done removing action listeners
		return;
	}
	
	/**
	 * Cancels activities from other games- stops currently running computer-controlled games 
	 * and removes panels for adding pieces
	 */
	private void initializeSystem() {
		
		// stop computer-managed games
		autoGameMngr.stopAutomaticGame();
		specialGameMngr.stopSpecialGame();
		
		// remove panels for adding pieces
		removePanel(piecePanel);
		removePanel(kingsPanel);
	}
	
	// public methods	
	
	/**
	 * Initializes system, sets the game board and the board buttons.
	 * Sets the display manager for the game
	 */
	@Override
	public void setTwoPlayersGame() {

		// initialize system
		initializeSystem();
		
		// remove action listeners from board buttons
		removeListeners();
		
		// set board
		twoPlayerMngr.setBoard();
		
		// set buttons
		twoPlayerMngr.setButtons(boardButtons, params.mark);
		
		// set display manager
		gameManager.setDisplayManager(twoPlayerMngr);
		
		// display board
		twoPlayerMngr.displayBoard();
	}

	/**
	 * Initializes system, sets the game board and the board buttons.
	 * Sets the display manager for the game
	 */
	@Override
	public void setSinglePlayerGame() {

		// initialize system
		initializeSystem();
		
		// remove action listeners from board buttons
		removeListeners();
		
		// set board
		singlePlayerMngr.setBoard();
		
		// set buttons
		singlePlayerMngr.setButtons(boardButtons, params.mark);
		
		// set display manager
		gameManager.setDisplayManager(singlePlayerMngr);
		
		// display board
		singlePlayerMngr.displayBoard();
	}
	
	/**
	 * Initializes system, removes action listeners from board buttons
	 * and adds to game display's frame panel to add pieces
	 */
	@Override
	public void setPartialGameBoard() {

		// initialize system
		initializeSystem();
		
		// remove action listeners from board buttons
		removeListeners();
		
		// clear board
		gameManager.clearBoard();
		
		// display cleared board
		twoPlayerMngr.displayBoard();
		
		// add panels to the frame
		frame.getContentPane().add(kingsPanel, BorderLayout.EAST);		
		frame.validate();
		
		// display adding instructions
		JOptionPane.showMessageDialog(null, "Please add Kings first, then the rest of the pieces", "Adding Pieces", JOptionPane.INFORMATION_MESSAGE);
	}
	
	/**
	 * Sets board buttons and sets the display manager for the game 
	 */
	@Override
	public void setPartialGame() {		
		
		// set buttons
		twoPlayerMngr.setButtons(boardButtons, params.mark);
		
		// set display manager
		gameManager.setDisplayManager(twoPlayerMngr);
	}
	
	/**
	 * Initializes system, sets the game board and the board buttons.
	 * Sets the display manager for the game and starts the automatic game
	 */
	@Override
	public void setAutomaticGame() {

		// initialize system
		initializeSystem();		
		
		// remove action listeners from board buttons
		removeListeners();
		
		// set board
		autoGameMngr.setBoard();
		
		// set buttons
		autoGameMngr.setButtons(boardButtons);
		
		// set display manager
		gameManager.setDisplayManager(autoGameMngr);
		
		// display board
		autoGameMngr.displayBoard();
		
		// start game
		autoGameMngr.runAutomaticGame(gameManager.getMaxTurns(), TURN_DURATION, TURN_INTERVAL);
	}

	/**
	 * Initializes system, sets the game board and the board buttons.
	 * Sets the display manager for the game and starts the special game
	 */
	@Override
	public void setSpecialGame() {

//		while (true) { //TODO: REMOVE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
			// initialize system
			initializeSystem();

			// remove action listeners from board buttons
			removeListeners();

			// set board
			specialGameMngr.setBoard();

			// set buttons
			specialGameMngr.setButtons(boardButtons);

			// set display manager
			gameManager.setDisplayManager(specialGameMngr);

			// display board
			specialGameMngr.displayBoard();

			// start game
			specialGameMngr.runSpecialGame(gameManager.getMaxTurns(), TURN_DURATION, TURN_INTERVAL);
//		}
	}
	
	/**
	 * Sets each button's color to default (according to its position).
	 * Uses game board to update locations of pieces, and updates buttons' icons accordingly
	 */
	public void displayBoard() {
		
		// total number of buttons of board 
		int buttonNumber = params.size * params.size;
		
		// scan all board buttons
		for (int i = 0 ; i < buttonNumber ; ++i) {
			
			// get current button from button list
			JButton currBtn = boardButtons.get(i);
			
			// set color back to default according to button's position
			if ((i/params.size) % 2 == 0) {
				currBtn.setBackground(params.dark);
				
				if (i % 2 == 0) {
					currBtn.setBackground(params.light);					
				} 
			} else {
				currBtn.setBackground(params.dark);
				
				if (i % 2 != 0) {
					currBtn.setBackground(params.light);					
				}
			}
		}
		
	}
	
	/**
	 * Uses the input-output manager object to notify user that game is
	 * in Check
	 */
	@Override
	public void displayCheck() { ioManager.notifyCheck(); }
	
	/**
	 * Removes action listeners from board buttons and uses input-output manager object to
	 * notify user that game is over 
	 */
	@Override
	public void gameOver(String msg) {
		
		// remove listeners from buttons
		removeListeners();		
		
		// notify that game is over
		ioManager.notifyGameOver(msg);
	}	
	
	/**
	 * Constructor- creates a TODO object name
	 */
	public UserIOgui(){

		// allocate a Controller object
		gameManager = new GameManager(/*this*/);
		
		// allocate icon map
		iconMap = new HashMap<String, ImageIcon>();
		
		// set map's entries with each piece's icon
		
		// Pawn icons
		iconMap.put(IconNames.WHITE+IconNames.PAWN, new ImageIcon(((new ImageIcon("src/view/chesskids/pawnhappywhite.png")).getImage()).getScaledInstance(150, 150, java.awt.Image.SCALE_SMOOTH)));
		iconMap.put(IconNames.BLACK+IconNames.PAWN, new ImageIcon(((new ImageIcon("src/view/chesskids/black-pawn.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
		
		// Knight icons
		iconMap.put(IconNames.WHITE+IconNames.KNIGHT, new ImageIcon(((new ImageIcon("src/view/chesskids/284.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
		iconMap.put(IconNames.BLACK+IconNames.KNIGHT, new ImageIcon(((new ImageIcon("src/view/chesskids/black-knight.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
		
		// Rook icons
		iconMap.put(IconNames.WHITE+IconNames.ROOK, new ImageIcon(((new ImageIcon("src/view/chesskids/rookhuggingwhite.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
		iconMap.put(IconNames.BLACK+IconNames.ROOK, new ImageIcon(((new ImageIcon("src/view/chesskids/rook giving a thumbs up black.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
		
		// Bishop icons
		iconMap.put(IconNames.WHITE+IconNames.BISHOP, new ImageIcon(((new ImageIcon("src/view/chesskids/white-bishop.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
		iconMap.put(IconNames.BLACK+IconNames.BISHOP, new ImageIcon(((new ImageIcon("src/view/chesskids/black-bishop.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
		
		// King icons
		iconMap.put(IconNames.WHITE+IconNames.KING, new ImageIcon(((new ImageIcon("src/view/chesskids/white-king.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
		iconMap.put(IconNames.BLACK+IconNames.KING, new ImageIcon(((new ImageIcon("src/view/chesskids/king thinking black.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
		
		// Queen icons
		iconMap.put(IconNames.WHITE+IconNames.QUEEN, new ImageIcon(((new ImageIcon("src/view/chesskids/white-queen.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
		iconMap.put(IconNames.BLACK+IconNames.QUEEN, new ImageIcon(((new ImageIcon("src/view/chesskids/black-queen.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
		
		// Creature icons
		iconMap.put(IconNames.CREATURE, new ImageIcon(((new ImageIcon("src/view/hulk-from-the-movie.jpg")).getImage()).getScaledInstance(100, 100, java.awt.Image.SCALE_SMOOTH)));
		iconMap.put(IconNames.CREATURE_COLLIDE, new ImageIcon(((new ImageIcon("src/view/fire-ball-icon-md.png")).getImage()).getScaledInstance(100, 100, java.awt.Image.SCALE_SMOOTH)));
		iconMap.put(IconNames.CREATURE_ATTACK, new ImageIcon(((new ImageIcon("src/view/hulk-2.jpg")).getImage()).getScaledInstance(120, 120, java.awt.Image.SCALE_SMOOTH)));
		
//		iconMap.put("white_Pawn", new ImageIcon(((new ImageIcon("src/view/chesskids/pawnhappywhite.png")).getImage()).getScaledInstance(150, 150, java.awt.Image.SCALE_SMOOTH)));
//		iconMap.put("black_Pawn", new ImageIcon(((new ImageIcon("src/view/chesskids/black-pawn.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
//		iconMap.put("white_Knight", new ImageIcon(((new ImageIcon("src/view/chesskids/284.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
//		iconMap.put("black_Knight", new ImageIcon(((new ImageIcon("src/view/chesskids/black-knight.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
//		iconMap.put("white_Rook", new ImageIcon(((new ImageIcon("src/view/chesskids/rookhuggingwhite.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
//		iconMap.put("black_Rook", new ImageIcon(((new ImageIcon("src/view/chesskids/rook giving a thumbs up black.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
//		iconMap.put("white_Bishop", new ImageIcon(((new ImageIcon("src/view/chesskids/white-bishop.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
//		iconMap.put("black_Bishop", new ImageIcon(((new ImageIcon("src/view/chesskids/black-bishop.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
//		iconMap.put("white_King", new ImageIcon(((new ImageIcon("src/view/chesskids/white-king.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
//		iconMap.put("black_King", new ImageIcon(((new ImageIcon("src/view/chesskids/king thinking black.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
//		iconMap.put("white_Queen", new ImageIcon(((new ImageIcon("src/view/chesskids/white-queen.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
//		iconMap.put("black_Queen", new ImageIcon(((new ImageIcon("src/view/chesskids/black-queen.png")).getImage()).getScaledInstance(90, 90, java.awt.Image.SCALE_SMOOTH)));
//		iconMap.put("creature", new ImageIcon(((new ImageIcon("src/view/hulk-from-the-movie.jpg")).getImage()).getScaledInstance(100, 100, java.awt.Image.SCALE_SMOOTH)));
//		iconMap.put("creature-collide", new ImageIcon(((new ImageIcon("src/view/fire-ball-icon-md.png")).getImage()).getScaledInstance(100, 100, java.awt.Image.SCALE_SMOOTH)));
//		iconMap.put("creature-attack", new ImageIcon(((new ImageIcon("src/view/hulk-2.jpg")).getImage()).getScaledInstance(120, 120, java.awt.Image.SCALE_SMOOTH)));
		
		// set board parameters
		params = new BoardParams();
		params.light = Color.WHITE;
		params.dark = Color.DARK_GRAY;
		params.mark = Color.ORANGE;
		params.size = 8;
		
		// initialize board 
		initialize(params);

		// allocate game managers
		ioManager = new ContactUser();
		twoPlayerMngr = new TwoPlayerDisplayTool(iconMap, params.light, params.dark, params.size, gameManager.getHumanControlledGameManager(), boardButtons, gameManager.getGameBoard(), this);
		singlePlayerMngr = new SinglePlayerDisplayTool(iconMap, params.light, params.dark, params.size, gameManager.getHumanControlledGameManager(), boardButtons, gameManager.getGameBoard(), this);
		autoGameMngr = new AutoGameDisplayTool(iconMap, params.light, params.dark, params.size, gameManager.getComputerControlledGameManager(), boardButtons, gameManager.getGameBoard());
		specialGameMngr = new SpecialGameDisplayTool(iconMap, params.light, params.dark, params.size, gameManager.getComputerControlledGameManager(), boardButtons, gameManager.getGameBoard());
		
		// create panel for adding pieces
		piecePanel = new AddPiecePanel(this, gameManager, gameManager.getMaxPiecesPartialGame(), this);
		
		// create panel for adding Kings
		kingsPanel = new AddKingsPanel(this, gameManager, piecePanel);
		
		// display game board
		displayBoard();
	}

	/**
	 * Gets type and color, and sets button at given position on board with matching icon
	 */
	@Override
	public void setButtonIcon(String pieceType, Point buttonPosition, boolean isFirstPlayer) {
		
		// get button to be updated
		int index = (buttonPosition.x-1)*params.size + buttonPosition.y-1; 
		JButton btnToUpdate = boardButtons.get(index);
		
		// update button icon
		if (isFirstPlayer) {
			
			// add a white piece
			btnToUpdate.setIcon(iconMap.get("white_" + pieceType));
			
		} else {
			
			// add a black piece
			btnToUpdate.setIcon(iconMap.get("black_" + pieceType));	
		}
				
		// update change on frame
		frame.validate();
	}
	
	/**
	 * Gets a panel and adds it to game display's frame
	 */
	@Override
	public void addPanel(JPanel panelToAdd) { 
		
		// add panel to frame
		frame.getContentPane().add(panelToAdd, BorderLayout.EAST); 
	}
	
	/**
	 * Removes given panel from game display's frame
	 */
	@Override
	public void removePanel(JPanel panelToRemove) { 
	
		// check if null was given
		if (panelToRemove == null) {
			
			// do nothing
			return;
		}
		
		// remove panel from frame
		frame.remove(panelToRemove);
		frame.validate();
	}
	
	/**
	 * Validate game display's frame
	 */
	@Override	
	public void validateFrame() { 
		
		// validate frame
		frame.validate(); 
	}
	
	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					
					UserIOgui window = new UserIOgui();
					window.frame.setVisible(true);

				} catch (Exception e) {
					e.printStackTrace();
				}
				
				
			}
		});
	}
}